#include <gst/gst.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "getch.h"
#include "playlist.h"
#include "uri.h"

typedef enum  {
	FIRST,
	NEXT,
	BACK,
	LAST,
	TNEXT,
	TBACK
}cur_t;

playlist* pl;
double volume = 1.0;
char **gargv;
int gargc; 
static GMainLoop *loop;

void play(GstElement* player, cur_t cur){
	static int play_list_pos;
	char *m;
	
	switch (cur){
		case FIRST:
			pl = pl->first(pl);
			
			break;
		case LAST:
			pl = pl->last(pl);
			break;
		case BACK:
			if(pl->previous(pl))
			  pl = pl->previous(pl);
			break;
		case NEXT:
			pl = pl->next(pl);
			break;
		case TNEXT:
			{
				int s = 10;
				while(s-- && pl)
					pl = pl->next(pl);
			}
			break;
		case TBACK:
			{
				int s = 10;
				while(s--){
					if (pl->previous(pl)){
						pl = pl->previous(pl);
					}
				}
			}
			break;
			

	}

	{
		if (!pl){
			g_main_loop_quit(loop);
			return;
		}
		gchar* uri;
		gchar* dname;
		if (!(uri = (gchar*) uri_parse_scheme(pl->filename))){
			g_free(uri);
			if (!g_path_is_absolute(pl->filename)){
				dname = g_get_current_dir();
				uri = g_malloc( strlen(dname) + sizeof("file://") + strlen(pl->filename) + 2);
				strcpy(uri,"file://");
				strcat(uri,dname);
				strcat(uri,"/");
				g_free(dname);
			}
			else {
				uri = g_malloc( sizeof("file://") + strlen(pl->filename) + 1);
				strcpy(uri,"file://");
			}
			strcat(uri,pl->filename);
		}
		else {
			uri = g_malloc(strlen(pl->filename) + 1);
			strcpy(uri,pl->filename);
		}
		gst_element_set_state (GST_ELEMENT (player), GST_STATE_NULL);
		
		dname = (gchar*) uri_escape_string(uri,URI_RESERVED_CHARS_ALLOWED_IN_PATH,TRUE);

		g_free(uri);
		uri = dname;
		//g_print("\n\nPlaying %s\n", uri);

		g_object_set(player,"uri",uri,NULL);
		g_print("\n\nPlaying %s\n", pl->filename);
		g_print("Playing %d of %d\n", pl->shuf +1, pl->fl->count);
		g_free(uri);
		 /* start playback */
  		gst_element_set_state (GST_ELEMENT (player), GST_STATE_PLAYING);
		
	}

}


static gboolean process_events (GstBus     *bus,
		 GstMessage *message,
		  GstElement* player) 
{
	static char stored_message[255] = {0};
	static int times = 0;
	

	switch (GST_MESSAGE_TYPE(message)) {
		case GST_MESSAGE_EOS:
		case GST_MESSAGE_ERROR:
			play(player,NEXT);
			break;
		case GST_MESSAGE_TAG:
		{
   			GstTagList *taglist=gst_tag_list_new();
	        gst_message_parse_tag(message, &taglist);
	        char* s;
         	GValue *val = (GValue*)gst_tag_list_get_value_index (taglist, GST_TAG_ARTIST, 0);
			if (val){
				s = (char*)g_value_get_string (val);
	        	g_print ("Artist: %s\n", s);
	        	pl->artist = malloc(strlen(s) + 1);
	        	strcpy(pl->artist,s);
			}
	        if (val = (GValue*)gst_tag_list_get_value_index (taglist, GST_TAG_ALBUM, 0)){
	        	s = (char*)g_value_get_string (val);
	        	g_print ("Album: %s\n", s);
	        	pl->album = malloc(strlen(s) + 1);
	        	strcpy(pl->album,s);
			}
	        if (val = (GValue*)gst_tag_list_get_value_index(taglist, GST_TAG_TITLE, 0)){
	        	s = (char*)g_value_get_string (val);
	        	g_print ("Title: %s\n", s);
	        	pl->title = malloc(strlen(s) + 1);
	        	strcpy(pl->title,s);
	        }
	        break;
		}
			
		default:
			if (strcmp(stored_message,GST_MESSAGE_TYPE_NAME(message))) 
			{
				//g_print("\nGot %s message\n", GST_MESSAGE_TYPE_NAME(message));
				fflush(stdout);
				strcpy(stored_message,(char*)GST_MESSAGE_TYPE_NAME(message));
				times=0;
			}
			else {
		
			//g_print("\rGot %s message: %d", GST_MESSAGE_TYPE_NAME(message),times++);
			}

	}
	return TRUE;

}


static gboolean print_pos(GstElement* pipeline){
 	GstFormat fmt = GST_FORMAT_TIME;
  	gint64 pos, len;

  	if (gst_element_query_position (pipeline, &fmt, &pos)
    	&& gst_element_query_duration (pipeline, &fmt, &len)) {
    	g_print ("Time: %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT "      \r" ,
	    	 GST_TIME_ARGS (pos), GST_TIME_ARGS (len));
  	}

  	return TRUE;
}

void pause_play(GstElement* player){
	GstState state;
	
	
	gst_element_get_state( GST_ELEMENT(player), &state, NULL, GST_CLOCK_TIME_NONE);
	
	if (state == GST_STATE_PLAYING) {
		gst_element_set_state( GST_ELEMENT(player), GST_STATE_PAUSED);
	}
  	else {
  		gst_element_set_state (GST_ELEMENT (player), GST_STATE_PLAYING);
  	}
    if (gst_element_get_state (player, NULL, NULL, 0) != GST_STATE_CHANGE_SUCCESS){
      printf( "Gstreamer: change state failed!!!\n");
      return;
    }

}

int
getch_main (GIOChannel* iochannel, GIOCondition condition,
	      GstElement* pipeline)
{
	//pthread_mutex_t cs_mutex = PTHREAD_MUTEX_INITIALIZER;
	#define RIGHT 4414235
	#define LEFT 4479771
	#define UP 4283163
	#define DOWN 4348699
	#define SPACE ' '
	#define ASTERISK '*'
	#define SLASH '/'
	#define ESC 27
	#define PG_DOWN 2117491483
	#define PG_UP 2117425947
	#define RETURN 10
	#define CRIGHT 17205
	#define CLEFT 17461
	#define CUP 16693
	#define CDOWN 16949
	#define S 's': case 'S'
	
	
	gint64 pos;
	GstFormat fmt = GST_FORMAT_TIME;
	gst_element_query_position (pipeline, &fmt, &pos);
	

	

	#define ZERO_LIMIT( time) ( time < 0 ? 0: time)

	unsigned long int key;
	gint64 time = 0;
	//printf ("(*) Teste para funcao getch(), pressione  para sair!\n");
	if (!kbhit())
		return TRUE;
	//while (1){
		
		key = getch();
		
		//entering critical section
 		//pthread_mutex_lock( &cs_mutex );
 

		switch (key){
			case LEFT:
				time = ZERO_LIMIT(-5 * GST_SECOND + pos);
				
  				if (!gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
                  GST_SEEK_TYPE_SET, time,GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) 
    				g_print ("Seek failed!\n");
    			break;
    		case RIGHT:
    			time = ZERO_LIMIT(5 * GST_SECOND + pos);
    			if (!gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
                  GST_SEEK_TYPE_SET, time ,GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) 
    				g_print ("Seek failed!\n");
    			break;
    		case UP:
    			time = ZERO_LIMIT(20 * GST_SECOND + pos);
    			if (!gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
                  GST_SEEK_TYPE_SET, time ,GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) 
    				g_print ("Seek failed!\n");
    			break;
    		case DOWN:
    			time = ZERO_LIMIT(-20 * GST_SECOND + pos);
    		    if (!gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
                  GST_SEEK_TYPE_SET, time ,GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) 
    				g_print ("Seek failed!\n");
    			break;
    		case PG_UP:
    			time = ZERO_LIMIT(60 * GST_SECOND + pos);
    		    if (!gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
                  GST_SEEK_TYPE_SET, time ,GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) 
    				g_print ("Seek failed!\n");
    			break;
    		case PG_DOWN:
    			time = ZERO_LIMIT(-60 * GST_SECOND + pos);
    		    if (!gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
                  GST_SEEK_TYPE_SET, time ,GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) 
    				g_print ("Seek failed!\n");
    			break;
    		case SPACE:
    			pause_play(pipeline);
    			break;
    		case ASTERISK:
    			volume += 0.2;
    			volume = volume >10.0? 10.0: volume;
    		
    			g_object_set(pipeline,"volume",volume,NULL);
    			g_print("\nVolume: %0.2f\n", volume);
    			break;
    		case SLASH:
    		    volume -= 0.2;
    		    volume = volume < 0 ? 0 : volume;
    			g_object_set(pipeline,"volume",volume,NULL);
    			g_print("\nVolume: %0.2f\n", volume);
    			break;
    		
    		case ESC:
    			exit(0);
    			break;
    		case CLEFT:
    			play(pipeline,BACK);
    			break;
    		case CRIGHT:
    			play(pipeline,NEXT);
    			break;
    		case CUP:
    			play(pipeline,TNEXT);
    			break;
    		case CDOWN:
    			play(pipeline,TBACK);
    			break;
    		case S:
    			pl->shuffle(pl);
    		
    		default:
    			printf("%c",key);

		}
		//leaving critical section
	
		//pthread_mutex_unlock( &cs_mutex );
		
	//}
	//pthread_mutex_destroy( &cs_mutex );
	
	return TRUE	;
}



int
main (int   argc, char *argv[])
{
	GstElement *player;
  	GstBus *bus;
  	GIOChannel* in;

  	GError *err = NULL;
  	/* init */
  	gargc = argc;
  	gargv = argv;
	gst_init (&argc, &argv);
	
	if (argc < 2){
		printf(
		" yplayer: http://code.google.com/p/yplayer \n"
		"\t usage: %s /path/to/musics/* path/to/another/musics/*\n",argv[0]);
		return 0;
	}

  	/* create player */
  	player = gst_element_factory_make ("playbin", "player");


  	/* set the source audio file */
  	pl = playlist_argv(argc,argv);
  	play(player, FIRST);
  	
  	



  	bus = gst_pipeline_get_bus(GST_PIPELINE(player));
  	gst_bus_add_watch(bus,(void*)process_events,player);
	prepare_fd_stdin();
  	g_timeout_add(100, (GSourceFunc) print_pos, player);
  	//g_timeout_add(50,(GSourceFunc) getch_main, player);
  	g_object_set(player,"volume",volume, NULL);
  	in = g_io_channel_unix_new (fileno(stdin));
  	g_io_add_watch(in,G_IO_IN,(void *)getch_main, player);
  	//pthread_t *thread;
	//g_thread_create((void *)getch_main, player, FALSE, &err); 
  	loop = g_main_loop_new (NULL, FALSE);
  	g_main_loop_run (loop);

  	g_main_loop_unref(loop);
}

